home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / Managed / DirectInput / ActionBasic / ActionBasicApp.cs < prev    next >
Encoding:
Text File  |  2004-09-27  |  20.8 KB  |  486 lines

  1. //-----------------------------------------------------------------------------
  2. // File: ActionBasicApp.cs
  3. //
  4. // Desc: The ActionBasic sample is intended to be an introduction to action mapping, 
  5. //       and illustrates a step by step approach to creating an action mapped 
  6. //       application.
  7. //
  8. // Copyright (c) Microsoft Corporation. All rights reserved
  9. //-----------------------------------------------------------------------------
  10. using System;
  11. using System.Threading;
  12. using System.Collections;
  13. using System.Windows.Forms;
  14. using DirectInput = Microsoft.DirectX.DirectInput;
  15. using Microsoft.DirectX.DirectInput;
  16. using Microsoft.DirectX;
  17.  
  18. namespace ActionBasic
  19. {
  20.     #region Step 1: Define the game actions.
  21.     // ****************************************************************************
  22.     // Step 1: Define the game actions. 
  23.     //         
  24.     // One of the big advantages of using action mapping to assist with game input
  25.     // is that it allows you to handle input in terms of game actions instead of
  26.     // device objects. For instance, this sample defines a small set of actions
  27.     // appropriate for a simple hand to hand combat game. When polling for user
  28.     // input, the data can be handled in terms of kicks and punches instead of
  29.     // button presses, axis movements, and keystrokes. This also allows to user
  30.     // to customize the controls without any further effort on the part of the
  31.     // developer.
  32.     //
  33.     // Each game action will be represented within your program as a 32 bit value.
  34.     // For this sample, the game actions correspond to indices within an array.
  35.     // ****************************************************************************
  36.     #endregion
  37.  
  38.     
  39.     
  40.     
  41.     /// <summary>
  42.     /// Game action constants
  43.     /// </summary>
  44.     enum GameActions 
  45.     {
  46.         Walk,              // Separate inputs are needed in this case for
  47.         WalkLeft,          //   Walk/Left/Right because the joystick uses an
  48.         WalkRight,         //   axis to report both left and right, but the
  49.         Block,             //   keyboard will use separate arrow keys.
  50.         Kick, 
  51.         Punch, 
  52.         TheDeAppetizer,    // "The De-Appetizer" represents a special move
  53.         Apologize,         //   defined by this game.
  54.         Quit,
  55.         NumOfActions       // Not an action; conveniently tracks number of
  56.     };                     //   actions.  Keep this as the last item.
  57.  
  58.  
  59.  
  60.  
  61.     /// <summary>
  62.     /// Convenience wrapper for device state
  63.     /// </summary>
  64.     public class DeviceState
  65.     {
  66.         public DirectInput.Device Device; //Reference to the device
  67.  
  68.         public string Name;           //Friendly name of the device      
  69.         public bool   IsAxisAbsolute; //Relative x-axis data flag
  70.         public int[]  InputState;     //Array of the current input values
  71.         public int[]  PaintState;     //Array of the current paint values
  72.         public bool[] IsMapped;       //Flags whether action was successfully mapped  
  73.                                           
  74.         /// <summary>
  75.         /// Constructor
  76.         /// </summary>
  77.         public DeviceState()
  78.         {
  79.             InputState = new int[(int)GameActions.NumOfActions];
  80.             PaintState = new int[(int)GameActions.NumOfActions];
  81.             IsMapped   = new bool[(int)GameActions.NumOfActions];
  82.         }
  83.     };
  84.  
  85.  
  86.  
  87.  
  88.     /// <summary>
  89.     /// The application class is responsible for initializing DirectInput, 
  90.     /// instantiating the user interface, and launching the game loop.
  91.     /// </summary>
  92.     public class ActionBasicApp
  93.     {
  94.         // Constants
  95.         private readonly Guid       AppGuid   = new Guid("ED48D6E8-91D1-4cf6-9ED3-BB327F76F17D");  
  96.         
  97.         // Friendly names for action constants are used by directInput for the
  98.         // built-in configuration UI
  99.         private readonly string[] ActionNames = 
  100.         {
  101.             "Walk left/right",
  102.             "Walk left",
  103.             "Walk right",
  104.             "Block",
  105.             "Kick",
  106.             "Punch",
  107.             "\"The De-Appetizer\"",
  108.             "Apologize",
  109.             "Quit"
  110.         };
  111.  
  112.         // member variables
  113.         private ActionFormat      DIActionFormat = new ActionFormat();
  114.         private ArrayList         DeviceStates   = new ArrayList();    
  115.         
  116.         /// <summary>
  117.         /// The user interface object
  118.         /// </summary>
  119.         public ActionBasicUI      UserInterface;
  120.         
  121.         /// <summary>
  122.         /// Constructor
  123.         /// </summary>
  124.         public ActionBasicApp()
  125.         {
  126.             // Instantiate the user interface object. The UI is given the
  127.             // list of game actions and a reference to the DeviceStates array,
  128.             // which the chart will use to display user input.
  129.             UserInterface = new ActionBasicUI(this, ActionNames, DeviceStates);
  130.             // Make sure the user interface has it's handle created before continuing
  131.             UserInterface.CreateControl();
  132.             UserInterface.Show();
  133.         }
  134.  
  135.         /// <summary>
  136.         /// Initialize the input devices
  137.         /// </summary>
  138.         private void InitializeInput()
  139.         {
  140.             #region Step 3: Enumerate Devices.
  141.             // ************************************************************************
  142.             // Step 3: Enumerate Devices.
  143.             // 
  144.             // Enumerate through devices according to the desired action map.
  145.             // Devices are enumerated in a prioritized order, such that devices which
  146.             // can best be mapped to the provided action map are returned first.
  147.             // ************************************************************************
  148.             #endregion
  149.  
  150.             // Setup the action format for actual gameplay
  151.             DIActionFormat.ActionMapGuid = AppGuid;
  152.             DIActionFormat.Genre = (int)FightingHandToHand.FightingHandToHand;
  153.             DIActionFormat.AxisMin = -99;
  154.             DIActionFormat.AxisMax = 99;
  155.             DIActionFormat.BufferSize = 16;
  156.             CreateActionMap(DIActionFormat.Actions);
  157.             
  158.             try
  159.             {
  160.                 // Enumerate devices according to the action format
  161.                 DeviceList DevList = Manager.GetDevices(DIActionFormat, EnumDevicesBySemanticsFlags.AttachedOnly);
  162.                 foreach (SemanticsInstance instance in DevList)
  163.                 {
  164.                     SetupDevice(instance.Device);
  165.                 }
  166.  
  167.             }
  168.             catch (DirectXException ex)
  169.             {
  170.                 UserInterface.ShowException(ex, "EnumDevicesBySemantics");
  171.             }
  172.         }
  173.  
  174.         /// <summary>
  175.         /// The main entry point for the application.
  176.         /// </summary>
  177.         [STAThread]
  178.         static void Main() 
  179.         {   
  180.             ActionBasicApp app = new ActionBasicApp();
  181.             using (app.UserInterface)
  182.             {
  183.                 app.InitializeInput();
  184.                 app.RunInputLoop();
  185.             }
  186.         }
  187.  
  188.  
  189.         
  190.         
  191.         /// <summary>
  192.         /// Handles device setup
  193.         /// </summary>
  194.         /// <param name="device">DirectInput Device</param>
  195.         private void SetupDevice(Device device)
  196.         {
  197.             // Create a temporary DeviceState object and store device information.
  198.             DeviceState state = new DeviceState();
  199.             device = device;   
  200.  
  201.             #region Step 4: Build the action map against the device
  202.             // ********************************************************************
  203.             // Step 4: Build the action map against the device, inspect the
  204.             //         results, and set the action map.
  205.             //
  206.             // It's a good idea to inspect the results after building the action
  207.             // map against the current device. The contents of the action map
  208.             // structure indicate how and to what object the action was mapped. 
  209.             // This sample simply verifies the action was mapped to an object on
  210.             // the current device, and stores the result. Note that not all actions
  211.             // will necessarily be mapped to an object on all devices. For instance,
  212.             // this sample did not request that QUIT be mapped to any device other
  213.             // than the keyboard.
  214.             // ********************************************************************
  215.             #endregion
  216.             try 
  217.             {
  218.                 // Build the action map against the device
  219.                 device.BuildActionMap(DIActionFormat, ActionMapControl.Default);
  220.             }
  221.             catch(DirectXException ex)
  222.             {
  223.                 UserInterface.ShowException(ex, "BuildActionMap");
  224.                 return;
  225.             }
  226.  
  227.             // Inspect the results
  228.             foreach (Action action in DIActionFormat.Actions)
  229.             {
  230.                 if ((int)action.How != (int)ActionMechanism.Error &&
  231.                     (int)action.How != (int)ActionMechanism.Unmapped)
  232.                 {
  233.                     state.IsMapped[(int)action.ApplicationData] = true;
  234.                 }
  235.             }
  236.  
  237.             // Set the action map
  238.             try
  239.             {
  240.                 device.SetActionMap(DIActionFormat, ApplyActionMap.Default);
  241.             }
  242.             catch(DirectXException ex)
  243.             {
  244.                 UserInterface.ShowException(ex, "SetActionMap");
  245.                 return;
  246.             }
  247.             
  248.             state.Device = device;
  249.  
  250.             // Store the device's friendly name for display on the chart
  251.             state.Name = device.DeviceInformation.InstanceName;
  252.  
  253.             // Store axis absolute/relative flag
  254.             state.IsAxisAbsolute = device.Properties.AxisModeAbsolute;
  255.  
  256.             DeviceStates.Add(state);
  257.             UserInterface.UpdateChart();
  258.             return;
  259.         }
  260.  
  261.  
  262.         
  263.         
  264.         /// <summary>
  265.         /// Launches the game loop
  266.         /// </summary>
  267.         private void RunInputLoop()
  268.         {
  269.             while(!UserInterface.IsDisposed)
  270.             {
  271.                 CheckInput();
  272.                 UserInterface.UpdateChart();                
  273.                 Application.DoEvents();
  274.             }
  275.         }
  276.  
  277.         
  278.         
  279.         
  280.         /// <summary>
  281.         /// Handle user input
  282.         /// </summary>
  283.         private void CheckInput()
  284.         {
  285.             // For each device gathered during enumeration, gather input. Although when
  286.             // using action maps the input is received according to actions, each device 
  287.             // must still be polled individually. Although for most actions your
  288.             // program will follow the same logic regardless of which device generated
  289.             // the action, there are special cases which require checking from which
  290.             // device an action originated.
  291.  
  292.             foreach (DeviceState state in DeviceStates)
  293.             {
  294.                 BufferedDataCollection dataCollection; 
  295.                 int curState = 0;
  296.  
  297.                 try 
  298.                 {
  299.                     state.Device.Acquire();
  300.                     state.Device.Poll();
  301.                     dataCollection = state.Device.GetBufferedData(); 
  302.                 }
  303.                 catch(DirectXException)
  304.                 {
  305.                     // GetDeviceData can fail for several reasons, some of which are
  306.                     // expected during a program's execution. A device's acquisition is not
  307.                     // permanent, and your program might need to reacquire a device several
  308.                     // times. Since this sample is polling frequently, an attempt to
  309.                     // acquire a lost device will occur during the next call to CheckInput.
  310.                     continue;
  311.                 }
  312.  
  313.                 if (null == dataCollection)
  314.                     continue;
  315.                 // For each buffered data item, extract the game action and perform
  316.                 // necessary game state changes. A more complex program would certainly
  317.                 // handle each action separately, but this sample simply stores raw
  318.                 // axis data for a WALK action, and button up or button down states for
  319.                 // all other game actions. 
  320.  
  321.                 // Relative axis data is never reported to be zero since relative data
  322.                 // is given in relation to the last position, and only when movement 
  323.                 // occurs. Manually set relative data to zero before checking input.
  324.                 if (!state.IsAxisAbsolute)
  325.                     state.InputState[(int)GameActions.Walk] = 0;
  326.  
  327.                 foreach (BufferedData data in dataCollection)
  328.                 {
  329.                     // The value stored in Action.ApplicationData equals the value stored in 
  330.                     // the ApplicationData property of the BufferedData class. For this sample
  331.                     // we selected these action constants to be indices into an array,
  332.                     // but we could have chosen these values to represent anything
  333.                     // from class objects to delegates.
  334.  
  335.                     curState = 0;
  336.  
  337.                     switch ((int)data.ApplicationData)
  338.                     {
  339.                         case (int)GameActions.Walk:  
  340.                         {
  341.                             // Axis data. Absolute axis data is already scaled to the
  342.                             // boundaries selected in the diACTIONFORMAT structure, but
  343.                             // relative data is reported as relative motion change 
  344.                             // since the last report. This sample scales relative data
  345.                             // and clips it to axis data boundaries.
  346.                             curState = data.Data;
  347.  
  348.                             if (!state.IsAxisAbsolute)
  349.                             {
  350.                                 // scale relative data
  351.                                 curState *= 5;
  352.  
  353.                                 // clip to boundaries
  354.                                 if (curState < 0)
  355.                                     curState = Math.Max(curState, DIActionFormat.AxisMin);
  356.                                 else
  357.                                     curState = Math.Min(curState, DIActionFormat.AxisMax);
  358.                             }
  359.  
  360.                             break;
  361.                         }
  362.  
  363.                         default:
  364.                         {
  365.                             // 0x80 is the DirectInput mask to determine whether
  366.                             // a button is pressed
  367.                             curState = ((data.Data & 0x80) != 0) ? 1 : 0;
  368.                             break;
  369.                         }
  370.                     }
  371.  
  372.                     state.InputState[ (int)data.ApplicationData ] = curState;
  373.                 }
  374.             }
  375.         }
  376.  
  377.         
  378.         
  379.         
  380.         /// <summary>
  381.         /// Creates the action map
  382.         /// </summary>
  383.         /// <returns>
  384.         /// An array of Action objects describing the game action 
  385.         /// to device object map for this application
  386.         /// </returns>
  387.         private void CreateActionMap(ActionCollection map)
  388.         {
  389.             #region Step 2: Define the action map.
  390.             // ****************************************************************************
  391.             // Step 2: Define the action map. 
  392.             //         
  393.             // The action map instructs DirectInput on how to map game actions to device
  394.             // objects. By selecting a predefined game genre that closely matches our game,
  395.             // you can largely avoid dealing directly with device details. For this sample
  396.             // we've selected the DIVIRTUAL_FIGHTING_HAND2HAND, and this constant will need
  397.             // to be selected into the DIACTIONFORMAT structure later to inform DirectInput
  398.             // of our choice. Every device has a mapping from genre actions to device
  399.             // objects, so mapping your game actions to genre actions almost guarantees
  400.             // an appropriate device configuration for your game actions.
  401.             //
  402.             // If DirectInput has already been given an action map for this GUID, it
  403.             // will have created a user map for this application 
  404.             // (C:\Program Files\Common Files\DirectX\DirectInput\User Maps\*.ini). If a
  405.             // map exists, DirectInput will use the action map defined in the stored user 
  406.             // map instead of the map defined in your program. This allows the user to
  407.             // customize controls without losing changes when the game restarts. If you 
  408.             // wish to make changes to the default action map without changing the 
  409.             // GUID, you will need to delete the stored user map from your hard drive
  410.             // for the system to detect your changes and recreate a stored user map.
  411.             // ****************************************************************************
  412.             #endregion
  413.             
  414.             // Device input (joystick, etc.) that is pre-defined by dinput according
  415.             // to genre type. The genre for this app is Action->Hand to Hand Fighting.
  416.             map.Add(CreateAction(GameActions.Walk,           FightingHandToHand.AxisLateral,    0, "Walk left/right"));
  417.             map.Add(CreateAction(GameActions.Block,          FightingHandToHand.ButtonBlock,    0, "Block"));
  418.             map.Add(CreateAction(GameActions.Kick,           FightingHandToHand.ButtonKick,     0, "Kick"));
  419.             map.Add(CreateAction(GameActions.Punch,          FightingHandToHand.ButtonPunch,    0, "Punch"));
  420.             map.Add(CreateAction(GameActions.TheDeAppetizer, FightingHandToHand.ButtonSpecial1, 0, "\"The De-Appetizer\""));
  421.             
  422.             // Map the apologize button to any button on the device. directInput
  423.             // defines several "Any-Control Constants" for mapping game actions to
  424.             // any device object of a particular type.
  425.             map.Add(CreateAction(GameActions.Apologize,      DInputHelper.ButtonAny(1),         0, "Apologize"));
  426.             
  427.             // Keyboard input mappings
  428.             map.Add(CreateAction(GameActions.WalkLeft,       Keyboard.Left,                  0, "Walk left"));
  429.             map.Add(CreateAction(GameActions.WalkRight,      Keyboard.Right,                 0, "Walk right"));
  430.             map.Add(CreateAction(GameActions.Block,          Keyboard.B,                     0, "Block"));
  431.             map.Add(CreateAction(GameActions.Kick,           Keyboard.K,                     0, "Kick"));
  432.             map.Add(CreateAction(GameActions.Punch,          Keyboard.P,                     0, "Punch"));
  433.             map.Add(CreateAction(GameActions.TheDeAppetizer, Keyboard.D,                     0, "\"The De-Appetizer\""));
  434.             map.Add(CreateAction(GameActions.Apologize,      Keyboard.A,                     0, "Apologize"));
  435.             
  436.             // The AppFixed constant can be used to instruct directInput that the
  437.             // current mapping can not be changed by the user.
  438.             map.Add(CreateAction(GameActions.Quit,           Keyboard.Q, ActionAttributeFlags.AppFixed, "Quit"));
  439.             
  440.             // Mouse input mappings
  441.             map.Add(CreateAction(GameActions.Walk,           Mouse.XAxis,                       0, "Walk"));
  442.             map.Add(CreateAction(GameActions.Punch,          Mouse.Button0,                     0, "Punch"));
  443.             map.Add(CreateAction(GameActions.Kick,           Mouse.Button1,                     0, "Kick"));
  444.         }
  445.  
  446.         
  447.         
  448.         
  449.         /// <summary>
  450.         /// Helper method to fill the action map during initialization
  451.         /// </summary>
  452.         private Action CreateAction(GameActions AppData, object Semantic, object Flags, string ActionName)
  453.         {
  454.             Action action = new Action();
  455.  
  456.             action.ApplicationData = (int)AppData;
  457.             action.Semantic   = (int)Semantic;
  458.             action.Flags      = (ActionAttributeFlags)Flags;
  459.             action.ActionName = ActionName;
  460.  
  461.             return action;
  462.         }
  463.  
  464.         
  465.         
  466.         
  467.         /// <summary>
  468.         /// Show the native device configuration UI
  469.         /// </summary>
  470.         public void ConfigureDevices()
  471.         {
  472.             ActionFormat[] formats = { DIActionFormat };
  473.  
  474.             ConfigureDevicesParameters cdParams = new ConfigureDevicesParameters();
  475.             cdParams.SetActionFormats(formats);
  476.             // Disable the parent
  477.             UserInterface.Enabled = false;
  478.             Manager.ConfigureDevices(cdParams, ConfigureDevicesFlags.Default);
  479.             // Re-enable the parent
  480.             UserInterface.Enabled = true;
  481.             // Bring it back to the front
  482.             UserInterface.BringToFront();
  483.         }
  484.     }
  485. }
  486.